using System;
using System.Collections.Generic;
using System.Text;
using InfFront.UICore;
using System.ComponentModel;
using System.IO;

namespace InfFront.InfoSources.Base
{
    /// <summary>
    /// Base IDataType implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.ExpandablePolymorphTypeConvertor, InfFront.InfoSources.Base.Design")]
    public abstract class BaseDataType : IDataType, System.Runtime.Serialization.ISerializable
    {
        #region Private Variables
        #endregion

        public enum Type { None, Boolean, DateTime, Float, Integer, Long, String, Enum, 
                           [Description("Image/Icon buffer or stream")] Image }

        public enum PersistenceType { Volatile, Constant }

        public BaseDataType()
        {
        }

        internal abstract Type Kind { get; }

        internal abstract IDataTypeTraits Traits { get; }

        public abstract object GetDefaultValue();

        #region ISerializable Members

        public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
        }

        #endregion
    }

    /// Base IFormattableDataPresenter IDataType implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.ExpandablePolymorphTypeConvertor, InfFront.InfoSources.Base.Design")]
    public abstract class BaseFormattableDataType : BaseDataType, IFormattableDataPresenter
    {
        #region Private Variables
        private string format = string.Empty;
        #endregion

        public BaseFormattableDataType()
        {
        }

        public BaseFormattableDataType(string format)
        {
            this.format = format;
        }

        public string Format
        {
            get { return format; }
            set { format = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Format", Format);
        }

        string IFormattableDataPresenter.GetFormat()
        {
            return Format;
        }
    }

    /// <summary>
    /// Base IStringDataDesc implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.StringDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class StringDataType : BaseFormattableDataType, IStringDataDesc
    {
        #region Private Variables
        private int length = -1;
        #endregion

        public StringDataType(string fmt)
            : base(fmt)
        {
        }

        public StringDataType(string fmt, int length)
            : this(fmt)
        {
            this.length = length;
        }

        [DefaultValue(-1)]
        public int Length
        {
            get { return length; }
            set { length = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Length", Length);
        }

        internal override Type Kind 
        {
            get { return Type.String; }
        }

        internal override IDataTypeTraits Traits 
        {
            get { return StringDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return null;
        }

        #region IStringDataDesc Members

        int IStringDataDesc.GetLength()
        {
            return Length;
        }

        #endregion
    }

    /// <summary>
    /// Base IIntDataDesc implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.IntDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class IntDataType : BaseFormattableDataType, IIntDataDesc
    {
        #region Private Variables
        private int minimum = int.MinValue;
        private int maximum = int.MaxValue;
        #endregion

        public IntDataType(string fmt)
            : base(fmt)
        {
        }

        public IntDataType(string fmt, int min, int max)
            : this(fmt)
        {
            this.minimum = min;
            this.maximum = max;
        }

        [DefaultValue(int.MinValue)]
        public int Minimum
        {
            get { return minimum; }
            set { minimum = value; }
        }

        [DefaultValue(int.MaxValue)]
        public int Maximum
        {
            get { return maximum; }
            set { maximum = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Minimum", Minimum);
            info.AddValue("Maximum", Maximum);
        }

        internal override Type Kind 
        {
            get { return Type.Integer; }
        }

        internal override IDataTypeTraits Traits 
        {
            get { return IntDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return new int();
        }

        #region IIntDataDesc Members

        int IIntDataDesc.GetMinimum()
        {
            return Minimum;
        }

        int IIntDataDesc.GetMaximum()
        {
            return Maximum;
        }

        #endregion
    }

    /// <summary>
    /// Base ILongDataDesc implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.LongDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class LongDataType : BaseFormattableDataType, ILongDataDesc
    {
        #region Private Variables
        private long minimum = long.MinValue;
        private long maximum = long.MaxValue;
        #endregion

        public LongDataType(string fmt)
            : base(fmt)
        {
        }

        public LongDataType(string fmt, long min, long max)
            : this(fmt)
        {
            this.minimum = min;
            this.maximum = max;
        }

        [DefaultValue(long.MinValue)]
        public long Minimum
        {
            get { return minimum; }
            set { minimum = value; }
        }

        [DefaultValue(long.MaxValue)]
        public long Maximum
        {
            get { return maximum; }
            set { maximum = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Minimum", Minimum);
            info.AddValue("Maximum", Maximum);
        }

        internal override Type Kind
        {
            get { return Type.Long; }
        }

        internal override IDataTypeTraits Traits
        {
            get { return LongDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return new long();
        }

        #region IIntDataDesc Members

        long ILongDataDesc.GetMinimum()
        {
            return Minimum;
        }

        long ILongDataDesc.GetMaximum()
        {
            return Maximum;
        }

        #endregion
    }

    /// <summary>
    /// Base IBoolDataType implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.BoolDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class BoolDataType : BaseFormattableDataType, IBoolDataDesc
    {
        public BoolDataType(string fmt)
            : base(fmt)
        {
        }

        internal override Type Kind 
        {
            get { return Type.Boolean; }
        }

        internal override IDataTypeTraits Traits 
        {
            get { return BoolDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return new bool();
        }
    }

    /// <summary>
    /// Image stream format
    /// </summary>
    public enum ImageStreamFormat { Png, Bmp };

    /// <summary>
    /// Base IImageDataType implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.ImageDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class ImageDataType : BaseDataType, IImageDataDesc
    {
        private ProxyTraits traits;

        public ImageDataType()
        {
            traits = new ProxyTraits(this);
            StreamFormat = ImageStreamFormat.Png;
        }

        public ImageDataType(ImageStreamFormat format)
        {
            traits = new ProxyTraits(this);
            StreamFormat = format;
        }

        [DefaultValue(ImageStreamFormat.Png)]
        public ImageStreamFormat StreamFormat
        {
            get;
            set;
        }

        internal override Type Kind
        {
            get { return Type.Image; }
        }

        internal override IDataTypeTraits Traits
        {
            get { return traits; }
        }

        public override object GetDefaultValue()
        {
            return null;
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("StreamFormat", StreamFormat);
        }

        internal Stream ConvertToPngStream(object data, int width, int height, IconStyle style)
        {
            Stream stream = null;
            var asImgSrc = data as IImageSource;
            if (asImgSrc != null)
                stream = asImgSrc.GetStretched(width, height, style);
            else
            {
                stream = data as Stream;
                if (stream == null)
                {
                    var bytes = data as byte[];
                    if (bytes == null)
                        throw new ArgumentException(string.Format("Icon/Image field value must be an IImageSource, Stream or byte[] instance. Actual value is '{1}'.", data));
                    stream = new MemoryStream(bytes);
                }
            }
            switch (StreamFormat)
            {
                case ImageStreamFormat.Png:
                    break;
                case ImageStreamFormat.Bmp:
                    {
                        var bmp = new System.Drawing.Bitmap(stream);
                        stream = new MemoryStream();
                        bmp.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                        stream.Seek(0, SeekOrigin.Begin);
                    }
                    break;
                default:
                    throw new NotSupportedException(string.Format("Unsupported image stream format: {0}", StreamFormat));
            }
            return stream;
        }

        #region IDataTypeTraits implementaion

        private class ProxyTraits : IDataTypeTraits
        {
            private ImageDataType owner;

            public ProxyTraits(ImageDataType owner)
            {
                this.owner = owner;
            }

            #region IDataTypeTraits Members

            public System.Type Type
            {
                get { return ImageDataTraits.Instance.Type; }
            }

            public object ReadData(BinaryReader reader)
            {
                return ImageDataTraits.Instance.ReadData(reader);
            }

            public void WriteData(object data, BinaryWriter writer)
            {
                var stream = owner.ConvertToPngStream(data, 16, 16, IconStyle.Normal);
                ImageDataTraits.Instance.WriteData(stream, writer);
            }

            #endregion
        }

        #endregion
    }

    /// <summary>
    /// Base IFloatDataDesc implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.FloatDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class FloatDataType : BaseFormattableDataType, IFloatDataDesc
    {
        #region Private Variables
        private double minimum = double.MinValue;
        private double maximum = double.MaxValue;
        #endregion

        public FloatDataType(string fmt)
            : base(fmt)
        {
        }

        public FloatDataType(string fmt, double min, double max)
            : this(fmt)
        {
            this.minimum = min;
            this.maximum = max;
        }

        [DefaultValue(double.MinValue)]
        public double Minimum
        {
            get { return minimum; }
            set { minimum = value; }
        }

        [DefaultValue(double.MaxValue)]
        public double Maximum
        {
            get { return maximum; }
            set { maximum = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Minimum", Minimum);
            info.AddValue("Maximum", Maximum);
        }

        internal override Type Kind 
        {
            get { return Type.Float; }
        }

        internal override IDataTypeTraits Traits 
        {
            get { return DoubleDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return new double();
        }

        #region IFloatDataDesc Members

        double IFloatDataDesc.GetMinimum()
        {
            return Minimum;
        }

        double IFloatDataDesc.GetMaximum()
        {
            return Maximum;
        }

        #endregion
    }

    /// <summary>
    /// Base IDateTimeDataDesc implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.DateTimeDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class DateTimeDataType : BaseFormattableDataType, IDateTimeDataDesc
    {
        #region Private Variables
        private DateTime minimum;
        private DateTime maximum;
        #endregion

        public DateTimeDataType(string fmt)
            : base(fmt)
        {
        }

        public DateTimeDataType(string fmt, DateTime min, DateTime max)
            : this(fmt)
        {
            this.minimum = min;
            this.maximum = max;
        }

        public DateTime Minimum
        {
            get { return minimum; }
            set { minimum = value; }
        }

        public DateTime Maximum
        {
            get { return maximum; }
            set { maximum = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("Minimum", Minimum);
            info.AddValue("Maximum", Maximum);
        }

        internal override Type Kind 
        {
            get { return Type.DateTime; }
        }

        internal override IDataTypeTraits Traits 
        {
            get { return DateTimeDataTraits.Instance; }
        }

        public override object GetDefaultValue()
        {
            return new DateTime();
        }

        #region IDateTimeDataDesc Members

        DateTime IDateTimeDataDesc.GetMinimum()
        {
            return Minimum;
        }

        DateTime IDateTimeDataDesc.GetMaximum()
        {
            return Maximum;
        }

        #endregion
    }

    /// <summary>
    /// CLR Enum data type implementation
    /// </summary>
    [Serializable, TypeConverter("InfFront.InfoSources.Base.Design.EnumDataDescTypeConverter, InfFront.InfoSources.Base.Design")]
    public class EnumDataType : BaseFormattableDataType, IStringDataDesc
    {
        #region Private Variables
        private System.Type type;
        private TypeTraits traits;
        #endregion

        public EnumDataType(string fmt)
            : base(fmt)
        {
            traits = new TypeTraits(this);
        }

        public EnumDataType(string fmt, System.Type enumType)
            : base(fmt)
        {
            this.type = enumType;
            traits = new TypeTraits(this);
        }

        public System.Type EnumType
        {
            get { return type; }
            set { type = value; }
        }

        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("EnumType", type);
        }

        internal override Type Kind
        {
            get { return Type.Enum; }
        }

        internal override IDataTypeTraits Traits
        {
            get { return traits; }
        }

        public override object GetDefaultValue()
        {
            return Enum.GetValues(EnumType).GetValue(0);
        }

        #region IStringDataDesc Members

        int IStringDataDesc.GetLength()
        {
            return -1;
        }

        #endregion

        private class TypeTraits : IDataTypeTraits
        {
            private EnumDataType owner;

            public TypeTraits(EnumDataType owner)
            {
                this.owner = owner;
            }

            #region IDataTypeTraits Members

            System.Type IDataTypeTraits.Type
            {
                get { return owner.EnumType; }
            }

            object IDataTypeTraits.ReadData(System.IO.BinaryReader reader)
            {
                var vStr = reader.ReadString();
                return Enum.Parse(owner.EnumType, vStr);
            }

            void IDataTypeTraits.WriteData(object data, System.IO.BinaryWriter writer)
            {
                writer.Write(data.ToString());
            }

            #endregion
        }
    }
}
